﻿using System;
using System.IO;

public class RAPPELZDATA_INDEX
{
    public String name;
    public String hash;
    public Int32 size;
    public Int32 DataID;
    public Int32 offset;
}

class RappelzDataCipher
{
    public static Byte[] Xor = new Byte[] { 
                         0x77, 0xE8, 0x5E, 0xEC, 0xB7, 0x4E, 0xC1, 0x87, 0x4F, 0xE6, 0xF5, 0x3C, 0x1F, 0xB3, 0x15, 0x43,
                         0x6A, 0x49, 0x30, 0xA6, 0xBF, 0x53, 0xA8, 0x35, 0x5B, 0xE5, 0x9E, 0x0E, 0x41, 0xEC, 0x22, 0xB8,
                         0xD4, 0x80, 0xA4, 0x8C, 0xCE, 0x65, 0x13, 0x1D, 0x4B, 0x08, 0x5A, 0x6A, 0xBB, 0x6F, 0xAD, 0x25,
                         0xB8, 0xDD, 0xCC, 0x77, 0x30, 0x74, 0xAC, 0x8C, 0x5A, 0x4A, 0x9A, 0x9B, 0x36, 0xBC, 0x53, 0x0A,
                         0x3C, 0xF8, 0x96, 0x0B, 0x5D, 0xAA, 0x28, 0xA9, 0xB2, 0x82, 0x13, 0x6E, 0xF1, 0xC1, 0x93, 0xA9,
                         0x9E, 0x5F, 0x20, 0xCF, 0xD4, 0xCC, 0x5B, 0x2E, 0x16, 0xF5, 0xC9, 0x4C, 0xB2, 0x1C, 0x57, 0xEE,
                         0x14, 0xED, 0xF9, 0x72, 0x97, 0x22, 0x1B, 0x4A, 0xA4, 0x2E, 0xB8, 0x96, 0xEF, 0x4B, 0x3F, 0x8E,
                         0xAB, 0x60, 0x5D, 0x7F, 0x2C, 0xB8, 0xAD, 0x43, 0xAD, 0x76, 0x8F, 0x5F, 0x92, 0xE6, 0x4E, 0xA7,
                         0xD4, 0x47, 0x19, 0x6B, 0x69, 0x34, 0xB5, 0x0E, 0x62, 0x6D, 0xA4, 0x52, 0xB9, 0xE3, 0xE0, 0x64,
                         0x43, 0x3D, 0xE3, 0x70, 0xF5, 0x90, 0xB3, 0xA2, 0x06, 0x42, 0x02, 0x98, 0x29, 0x50, 0x3F, 0xFD,
                         0x97, 0x58, 0x68, 0x01, 0x8C, 0x1E, 0x0F, 0xEF, 0x8B, 0xB3, 0x41, 0x44, 0x96, 0x21, 0xA8, 0xDA,
                         0x5E, 0x8B, 0x4A, 0x53, 0x1B, 0xFD, 0xF5, 0x21, 0x3F, 0xF7, 0xBA, 0x68, 0x47, 0xF9, 0x65, 0xDF,
                         0x52, 0xCE, 0xE0, 0xDE, 0xEC, 0xEF, 0xCD, 0x77, 0xA2, 0x0E, 0xBC, 0x38, 0x2F, 0x64, 0x12, 0x8D,
                         0xF0, 0x5C, 0xE0, 0x0B, 0x59, 0xD6, 0x2D, 0x99, 0xCD, 0xE7, 0x01, 0x15, 0xE0, 0x67, 0xF4, 0x32,
                         0x35, 0xD4, 0x11, 0x21, 0xC3, 0xDE, 0x98, 0x65, 0xED, 0x54, 0x9D, 0x1C, 0xB9, 0xB0, 0xAA, 0xA9,
                         0x0C, 0x8A, 0xB4, 0x66, 0x60, 0xE1, 0xFF, 0x2E, 0xC8, 0x00, 0x43, 0xA9, 0x67, 0x37, 0xDB, 0x9C,
                         0x77, 0xE8, 0x5E, 0xEC, 0xB7, 0x4E, 0xC1, 0x87, 0x4F, 0xE6, 0xF5, 0x3C, 0x1F, 0xB3, 0x15, 0x43,
                         0x6A, 0x49, 0x30, 0xA6, 0xBF, 0x53, 0xA8, 0x35, 0x5B, 0xE5, 0x9E, 0x0E, 0x41, 0xEC, 0x22, 0xB8,
                         0xD4, 0x80, 0xA4, 0x8C, 0xCE, 0x65, 0x13, 0x1D, 0x4B, 0x08, 0x5A, 0x6A, 0xBB, 0x6F, 0xAD, 0x25,
                         0xB8, 0xDD, 0xCC, 0x77, 0x30, 0x74, 0xAC, 0x8C, 0x5A, 0x4A, 0x9A, 0x9B, 0x36, 0xBC, 0x53, 0x0A,
                         0x3C, 0xF8, 0x96, 0x0B, 0x5D, 0xAA, 0x28, 0xA9, 0xB2, 0x82, 0x13, 0x6E, 0xF1, 0xC1, 0x93, 0xA9,
                         0x9E, 0x5F, 0x20, 0xCF, 0xD4, 0xCC, 0x5B, 0x2E, 0x16, 0xF5, 0xC9, 0x4C, 0xB2, 0x1C, 0x57, 0xEE,
                         0x14, 0xED, 0xF9, 0x72, 0x97, 0x22, 0x1B, 0x4A, 0xA4, 0x2E, 0xB8, 0x96, 0xEF, 0x4B, 0x3F, 0x8E,
                         0xAB, 0x60, 0x5D, 0x7F, 0x2C, 0xB8, 0xAD, 0x43, 0xAD, 0x76, 0x8F, 0x5F, 0x92, 0xE6, 0x4E, 0xA7,
                         0xD4, 0x47, 0x19, 0x6B, 0x69, 0x34, 0xB5, 0x0E, 0x62, 0x6D, 0xA4, 0x52, 0xB9, 0xE3, 0xE0, 0x64,
                         0x43, 0x3D, 0xE3, 0x70, 0xF5, 0x90, 0xB3, 0xA2, 0x06, 0x42, 0x02, 0x98, 0x29, 0x50, 0x3F, 0xFD,
                         0x97, 0x58, 0x68, 0x01, 0x8C, 0x1E, 0x0F, 0xEF, 0x8B, 0xB3, 0x41, 0x44, 0x96, 0x21, 0xA8, 0xDA,
                         0x5E, 0x8B, 0x4A, 0x53, 0x1B, 0xFD, 0xF5, 0x21, 0x3F, 0xF7, 0xBA, 0x68, 0x47, 0xF9, 0x65, 0xDF,
                         0x52, 0xCE, 0xE0, 0xDE, 0xEC, 0xEF, 0xCD, 0x77, 0xA2, 0x0E, 0xBC, 0x38, 0x2F, 0x64, 0x12, 0x8D,
                         0xF0, 0x5C, 0xE0, 0x0B, 0x59, 0xD6, 0x2D, 0x99, 0xCD, 0xE7, 0x01, 0x15, 0xE0, 0x67, 0xF4, 0x32,
                         0x35, 0xD4, 0x11, 0x21, 0xC3, 0xDE, 0x98, 0x65, 0xED, 0x54, 0x9D, 0x1C, 0xB9, 0xB0, 0xAA, 0xA9,
                         0x0C, 0x8A, 0xB4, 0x66, 0x60, 0xE1, 0xFF, 0x2E, 0xC8, 0x00, 0x43, 0xA9, 0x67, 0x37, 0xDB, 0x9C
    };

    public static System.Collections.Generic.List<RAPPELZDATA_INDEX> rappelz_data  = new System.Collections.Generic.List<RAPPELZDATA_INDEX>();

    public static void Open(String data000file)
    {
        rappelz_data.Clear();

        try
        {
            FileStream fs = new FileStream(data000file, FileMode.Open, FileAccess.Read);
            BinaryReader br = new BinaryReader(fs);

            UInt32 fsl = (UInt32)fs.Length;
            UInt32 cp = 0;
            Byte xors = 0;

            while (cp < fsl)
            {
                RAPPELZDATA_INDEX rappelz_index = new RAPPELZDATA_INDEX();
                Byte size = br.ReadByte();
                cp++;

                size ^= Xor[xors];
                xors++;
                Byte[] hash = br.ReadBytes(size);

                for (int i = 0; i < size; i++)
                {
                    hash[i] ^= Xor[xors];
                    xors++;
                    cp++;
                }

                Byte[] data = br.ReadBytes(8);

                for (int i = 0; i < 8; i++)
                {
                    data[i] ^= Xor[xors];
                    xors++;
                    cp++;
                }

                rappelz_index.name = RappelzNameCipher.Decrypt(SFM.BytesToString(hash));
                rappelz_index.hash = SFM.BytesToString(hash);
                rappelz_index.size = BitConverter.ToInt32(data, 4);
                rappelz_index.DataID = GetFileNumber(rappelz_index.hash) + 1;
                rappelz_index.offset = BitConverter.ToInt32(data, 0);
                rappelz_data.Add(rappelz_index);

            }
            fs.Close();
            fs.Dispose();
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.Message, "Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
        }
    }

    public static void DumpFile(string datapath, string dumppath, String name, Int32 offset, Int32 id, Int32 size)
    {
        FileStream fs = new FileStream(datapath + "\\" + "Data.00" + id, FileMode.Open, FileAccess.Read);
        fs.Seek(offset, SeekOrigin.Begin);
        if (!Encrypted(Path.GetExtension(name).Replace(".", "")))
        {
            BinaryReader br = new BinaryReader(fs);
            File.WriteAllBytes(dumppath + "\\" + name, br.ReadBytes(size));
            fs.Close();
            fs.Dispose();
            br.Close();
            br.Dispose();
        }
        else
        {
            BinaryReader br = new BinaryReader(fs);
            Byte[] data = br.ReadBytes(size);
            Byte xors = 0;
            for (int i = 0; i < size; i++)
            {
                data[i] ^= Xor[xors];
                xors++;
            }
            File.WriteAllBytes(dumppath + "\\" + name, data);
            fs.Close();
            fs.Dispose();
            br.Close();
            br.Dispose();

        }
    }

    public static void CreateData000(String data000file)
    {
        try
        {
            FileStream fs = File.Create(data000file);
            BinaryWriter bw = new BinaryWriter(fs);

            Byte xors = 0;

            foreach (RAPPELZDATA_INDEX rappelz_index in rappelz_data)
            {
                Byte size = (byte)rappelz_index.hash.Length;
                size ^= Xor[xors];
                xors++;
                bw.Write(size);

                Byte[] hash = SFM.StringToBytes(rappelz_index.hash);
                for (int i = 0; i < rappelz_index.hash.Length; i++)
                {
                    hash[i] ^= Xor[xors];
                    xors++;
                }
                bw.Write(hash);

                Byte[] offset = new Byte[4];
                Byte[] file_size = new Byte[4];
                for (int i = 0; i <= 3; i++) offset[i] = (byte)((rappelz_index.offset >> (i * 8)) & 0x000000FF);
                for (int i = 0; i <= 3; i++) file_size[i] = (byte)((rappelz_index.size >> (i * 8)) & 0x000000FF);
                Byte[] data = new Byte[8];
                for (int i = 0; i < 4; i++) data[i] = offset[i];
                for (int i = 4; i < 8; i++) data[i] = file_size[i - 4];
                for (int i = 0; i < 8; i++)
                {
                    data[i] ^= Xor[xors];
                    xors++;
                }
                bw.Write(data);
            }
            fs.Close();
            fs.Dispose();

        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show(ex.Message, "Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
        }
    }

    public static bool Encrypted(string ext)
    {
        if (ext == "dds") return false;
        if (ext == "tga") return false;
        if (ext == "naf") return false;
        if (ext == "nx3") return false;
        if (ext == "cob") return false;
        if (ext == "itc") return false;
        if (ext == "itm") return false;
        if (ext == "itb") return false;
        if (ext == "itc") return false;
        if (ext == "nfm") return false;
        if (ext == "pdb") return false;
        return true;
    }

    public static Int32 GetFileNumber(string pHash)
    {
        byte[] buff = System.Text.Encoding.ASCII.GetBytes(pHash.ToLower() + "0");

        int v3 = 0;
        int v30 = 0;
        int v31 = 0;
        int v29 = 0;
        if (buff[0] != 0)
        {
            while (buff.Length - 1 != v30)
            {
                v3 = buff[v30];
                v31 = v29 << 5;
                v31 -= v29;

                v31 += v3;
                v30++;
                v3 = buff[v30];
                v29 = v31;
            }

            if (v31 < 0)
                v29 = -v31;
        }
        int v32 = v29 % 8;
        return v32;
    }
}
